import { contextBridge, ipcRenderer, IpcRendererEvent } from 'electron';
import {
  IPtyExitEvent,
  IPtyProcessChangeEvent,
  IShellLaunchConfig,
  ITerminalError,
} from '../renderer/terminal-next';
import { IDisposable } from '../renderer/utils';

const electronHandler = {
  ipcRenderer: {
    // 监听 IPC 事件。用来监听主进程发送过来的消息。你可以通过 window.ipcRenderer.on('channel-name', callback) 来注册监听函数。
    on(...args: Parameters<typeof ipcRenderer.on>) {
      const [channel, listener] = args;
      return ipcRenderer.on(channel, (event, ...args) =>
        listener(event, ...args)
      );
    },
    // 移除 IPC 事件监听器。用来移除之前通过 on 方法注册的监听器。可以指定 channel 和 listener。
    off(...args: Parameters<typeof ipcRenderer.off>) {
      const [channel, ...omit] = args;
      return ipcRenderer.off(channel, ...omit);
    },
    // 发送 IPC 消息。用来发送消息到主进程。你可以通过 window.ipcRenderer.send('channel-name', data) 向主进程发送消息。
    send(...args: Parameters<typeof ipcRenderer.send>) {
      const [channel, ...omit] = args;
      return ipcRenderer.send(channel, ...omit);
    },
    // 调用 IPC 方法。用来发送一个请求给主进程，并等待返回一个响应。可以通过 window.ipcRenderer.invoke('channel-name', data) 调用主进程的 invoke 方法，并返回一个 Promise。
    invoke(...args: Parameters<typeof ipcRenderer.invoke>) {
      const [channel, ...omit] = args;
      return ipcRenderer.invoke(channel, ...omit);
    },

    subscribe(
      channel: string,
      listener: (...args: any[]) => void
    ): IDisposable {
      ipcRenderer.on(channel, listener);
      return {
        dispose() {
          ipcRenderer.off(channel, listener);
        },
      };
    },

    windowMinimize() {
      ipcRenderer.send('window:minimize');
    },
    windowMaximize() {
      ipcRenderer.send('window:maximize');
    },
    windowClose() {
      ipcRenderer.send('window:close');
    },

    logInfo: (...params: any[]) => ipcRenderer.send('log', 'info', ...params),
    logWarn: (...params: any[]) => ipcRenderer.send('log', 'warn', ...params),
    logError: (...params: any[]) => ipcRenderer.send('log', 'error', ...params),
    logDebug: (...params: any[]) => ipcRenderer.send('log', 'debug', ...params),
  },

  terminal: {
    generateSessionId: () => ipcRenderer.invoke('terminal:generateSessionId'),

    getOptions: () => ipcRenderer.invoke('terminal:getOptions'),

    check: (sessionIds: string[]) =>
      ipcRenderer.invoke('terminal:check', sessionIds),

    attachByLaunchConfig: (
      sessionId: string,
      cols: number,
      rows: number,
      launchConfig: IShellLaunchConfig
      // xterm: IXTerm
    ) => {
      return ipcRenderer.invoke(
        'terminal:attachByLaunchConfig',
        sessionId,
        cols,
        rows,
        launchConfig
        // xterm
      );
    },

    sendText: (sessionId: string, message: string) =>
      ipcRenderer.invoke('terminal:sendText', sessionId, message),

    resize: (sessionId: string, cols: number, rows: number) =>
      ipcRenderer.invoke('terminal:resize', sessionId, cols, rows),

    disposeById: (sessionId: string) =>
      ipcRenderer.invoke('terminal:disposeById', sessionId),

    getProcessId: (sessionId: string) =>
      ipcRenderer.invoke('terminal:getProcessId', sessionId),

    getOS: () => ipcRenderer.invoke('terminal:getOS'),

    getProfiles: (autoDetect: boolean) =>
      ipcRenderer.invoke('terminal:getProfiles', autoDetect),

    getDefaultSystemShell: () =>
      ipcRenderer.invoke('terminal:getDefaultSystemShell'),

    getCodePlatformKey: () => ipcRenderer.invoke('terminal:getCodePlatformKey'),

    getCwd: (sessionId: string) =>
      ipcRenderer.invoke('terminal:getCwd', sessionId),

    enter: (sessionId: string) =>
      ipcRenderer.invoke('terminal:enter', sessionId),
  },

  // 终端事件监听
  terminalEvents: {
    onError(callback: (error: ITerminalError) => void): IDisposable {
      const listener = (_event: any, error: ITerminalError) => callback(error);
      return electronHandler.ipcRenderer.subscribe(
        'terminal:onError',
        listener
      );
    },
    onExit(callback: (event: IPtyExitEvent) => void): IDisposable {
      const listener = (_event: any, event: IPtyExitEvent) => callback(event);
      return electronHandler.ipcRenderer.subscribe('terminal:onExit', listener);
    },
    onProcessChange(
      callback: (event: IPtyProcessChangeEvent) => void
    ): IDisposable {
      const listener = (_event: any, event: IPtyProcessChangeEvent) =>
        callback(event);
      return electronHandler.ipcRenderer.subscribe(
        'terminal:onProcessChange',
        listener
      );
    },

    onSessionData(
      sessionId: string,
      callback: (sessionId: string, data: string | ArrayBuffer) => void
    ): IDisposable {
      const listener = (_event, id: string, data: string | ArrayBuffer) => {
        if (id === sessionId) {
          callback(id, data);
        }
      };
      return electronHandler.ipcRenderer.subscribe(
        'terminal:onSessionData',
        listener
      );
    },

    onSessionExit(
      sessionId: string,
      callback: (sessionId: string, data: number) => void
    ): IDisposable {
      const listener = (_event, id: string, data: number) => {
        if (id === sessionId) {
          callback(id, data);
        }
      };
      return electronHandler.ipcRenderer.subscribe(
        'terminal:onSessionExit',
        listener
      );
    },
  },

  env: {
    BUILD_TARGET: process.env.BUILD_TARGET || 'default',
  },
};

contextBridge.exposeInMainWorld('electron', electronHandler);

export type ElectronHandler = typeof electronHandler;
